/*
 * DIM-SUM操作系统 -- 体系架构定时器实现
 *
 * Copyright (C) 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Dong Peng <w-pengdong@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <dim-sum/delay.h>
#include <dim-sum/types.h>
#include <dim-sum/init.h>
#include <dim-sum/smp.h>
#include <dim-sum/timer_device.h>
#include <dim-sum/timer.h>
#include <dim-sum/irq.h>
#include <dim-sum/irq_mapping.h>
#include <dim-sum/percpu.h>
#include <dim-sum/cpumask.h>
#include <dim-sum/sched.h>
#include <clocksource/riscv_arch_timer.h>
#include <asm/percpu.h>
#include <asm/io.h>
#include <asm/cputype.h>
#include <dim-sum/clocksource.h>
#include <asm/timex.h>
#include <asm/smp.h>
#include <asm/sbi.h>
#include <asm/csr.h>

#define IRQ_M_TIMER		7
#define IRQ_S_TIMER		5
u64 arch_timer_rate;

#define ARCH_CP15_TIMER	BIT(0)
#define ARCH_MEM_TIMER	BIT(1)

struct arch_timer {
	void __iomem *base;
	struct timer_device evt;
};

#define to_arch_timer(e) container_of(e, struct arch_timer, evt)

#define CNTTIDR		0x08
#define CNTTIDR_VIRT(n)	(BIT(1) << ((n) * 4))

#define CNTVCT_LO	0x08
#define CNTVCT_HI	0x0c
#define CNTFRQ		0x10
#define CNTP_TVAL	0x28
#define CNTP_CTL	0x2c
#define CNTV_TVAL	0x38
#define CNTV_CTL	0x3c

DEFINE_PER_CPU(struct timer_device, arch_timer_evt);

struct clocksource clocksource;

static cycle_t clocksource_read(struct clocksource *clksource)
{
    return get_cycles();
}

u32 arch_timer_get_rate(void)
{
	return arch_timer_rate;
}

static void
arch_timer_detect_rate(void)
{
	arch_timer_rate = 0x989680; // from dts arch_timer_get_cntfrq();
	loops_per_second = arch_timer_rate / HZ;

	pr_debug("定时器初始化完毕，时钟频率：%d。\n",
	arch_timer_rate);
}

enum ppi_nr {
	PHYS_SECURE_PPI,
	PHYS_NONSECURE_PPI,
	VIRT_PPI,
	HYP_PPI,
	MAX_TIMER_PPI
};


static __maybe_unused struct irq_configure arch_timer_of_desc[MAX_TIMER_PPI] = {

		[PHYS_SECURE_PPI] = {3, {1, 13, 1}, NULL},
		[PHYS_NONSECURE_PPI] = {3, {1, 14, 1} },
		[VIRT_PPI] = {3, {1, 11, 1} },
		[HYP_PPI] = {3, {1, 10, 1} },
	};


static __always_inline enum isr_result timer_handler(struct timer_device *evt)
{
	// printk("@@@@@: 123\n");
	evt->handle(evt);

	return ISR_SKIP;
}


void riscv_timer_interrupt(void)
{
	struct timer_device *evdev = this_cpu_var(&arch_timer_evt);
	csr_clear(CSR_IE, IE_TIE);
	// pr_info("%s %d stat:%lx SIE:%lx cpu:%ld\n", __FUNCTION__, __LINE__, 
	// arch_local_save_flags(), csr_read(CSR_IE), smp_processor_id());
	evdev->handle(evdev);
	// csr_set(CSR_IE, IE_TIE);
}

static __always_inline void timer_set_mode(const int access, int mode,
				  struct timer_device *clk)
{

}

static void arch_timer_set_mode_phys(enum clock_mode mode,
				     struct timer_device *clk)
{
	// timer_set_mode(ARM_TIMER_CP15_REAL_ACCESS, mode, clk);
}

static __always_inline void trigger_timer(unsigned long evt,
					   struct timer_device *clk)
{
	// pr_info("%s %d get_cycles():%lx evt:%lx\n", __FUNCTION__, __LINE__, get_cycles(), evt);
	csr_set(CSR_IE, IE_TIE);
	sbi_set_timer((uint64_t)(get_cycles() + evt));
}

static int arch_timer_set_next_event_phys(unsigned long evt,
					  struct timer_device *clk)
{
	trigger_timer(evt, clk);
	return 0;
}

static void __arch_timer_setup(unsigned type,
			       struct timer_device *clk)
{
	clk->features = CLK_FEAT_ONESHOT;

	clk->features |= CLK_FEAT_C3STOP;
	clk->name = "arch_sys_timer";
	clk->rating = 450;
	clk->irq = IRQ_M_TIMER;
	clk->set_mode = arch_timer_set_mode_phys;

	clk->trigger_timer = arch_timer_set_next_event_phys;

	clk->set_mode(CLK_MODE_POWERDOWN, clk);

	clk->min_counter = 0xf;
	clk->max_counter = 0x7fffffff;
	config_timer_device(clk, arch_timer_rate);
	register_timer_device(clk);

	clk->trigger_timer((unsigned long) arch_timer_rate / HZ, clk);
}

static int arch_timer_setup(struct timer_device *clk)
{
	__arch_timer_setup(0, clk);

	// enable_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI], 0);

	return 0;
}

static __maybe_unused int __init arch_timer_register(void)
{
	int err = 0;
	// int ppi;
	// ktime_t tick_period;
	// ppi = IRQ_S_TIMER;
	// tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
	
	// err = register_percpu_irq_handle(ppi, arch_timer_handler_phys,
	// 				 "arch_timer", &arch_timer_evt);

	// if (err) {
	// 	pr_err("arch_timer: can't register interrupt %d (%d)\n",
	// 	       ppi, err);
	// 	goto out_free;
	// }

	/**
	 * 在主核上立即启动定时器
	 */
	arch_timer_setup(this_cpu_var(&arch_timer_evt));

	return err;
}


void __init init_time_arch(void)
{
	unsigned long max_sec = 0x7fffffff;
	
	arch_timer_detect_rate();
	clocksource.read = clocksource_read;
	clocksource.rating = 300;
	clocksource.cycle_last = 0;
	clocksource.mask = (~0x0);
	BUG_ON (arch_timer_rate == 0);
	do_div(max_sec, arch_timer_rate);
	/**
	 * 至少一秒
	 */
	if (!max_sec)
		max_sec = 1;
	else if (max_sec > 600)
		max_sec = 600;

	/**
	 * 计算设备的mult，shift值，加快counter与ns的转换
	 */
	
	calc_mult_shift(&clocksource.mult, &clocksource.shift, arch_timer_rate, NSEC_PER_SEC, max_sec);
}

void __init init_timer_arch(void)
{
	arch_timer_register();
}

void arch_timer_secondary_init(void)
{

	// arch_timer_setup(this_cpu_ptr(&arch_timer_evt));
}
